`timescale 1ns/1ps
`default_nettype none

module cxy_gamma_table(
    // system signal
    input  wire         I_sclk,
    input  wire         I_rst_n,
    // config
    input  wire         I_cfg_gamma_enable,  // gamma使能
    input  wire         I_cfg_color_restore, // 色彩还原
    input  wire [3:0]   I_cfg_gamma_bit,     // gamma bit数, 0 - 1bit, ..., 15 - 16bit
    input  wire         I_cfg_gamma_incr,    // 低灰递增补偿
    // ram
    output wire         O_gamma_ram_rden,
    output wire [7:0]   O_gamma_ram_addr,
    input  wire [31:0]  I_gamma_ram_q,
    // data path
    input  wire         I_pixel_start,
    input  wire [7:0]   I_pixel_grey,
    output wire [15:0]  O_pixel_gamma
    );
//------------------------Parameter----------------------
localparam
    COE_FRAC = 14; // 系数小数位数

//------------------------Local signal-------------------
reg  [5:0]  step;
reg  [7:0]  grey_buf0;
reg  [7:0]  grey_buf1;
reg  [7:0]  grey_buf2;
reg  [7:0]  grey_buf3;
reg  [7:0]  grey_buf4;
reg  [15:0] base_r;
reg  [15:0] base_g;
reg  [15:0] base_b;
reg  [15:0] base_buf;
reg  [15:0] coe_r;
reg  [15:0] coe_g;
reg  [15:0] coe_b;
reg         comp0;
reg         comp1;
reg         comp2;
reg  [15:0] coe_buf;
reg  [31:0] result_buf;
wire [15:0] result_val;
reg  [15:0] incr_buf; // 低灰递增
reg  [15:0] gamma_buf;

//------------------------Instantiation------------------

//------------------------Body---------------------------
assign O_gamma_ram_rden = 1'b1;
assign O_gamma_ram_addr = I_pixel_grey;

assign O_pixel_gamma = gamma_buf;

//step[5:0]
always@(posedge I_sclk or negedge I_rst_n)
    if(!I_rst_n)
        step <= 'b0;
    else if(I_pixel_start)
        step <= {step[4:0], 1'b1};
    else
        step <= {step[4:0], 1'b0};

//grey_bufx[7:0]
always@(posedge I_sclk)
    begin
        grey_buf0 <= I_pixel_grey;
        grey_buf1 <= grey_buf0;
        grey_buf2 <= grey_buf1;
        grey_buf3 <= grey_buf2;
        grey_buf4 <= grey_buf3;
    end

//base_r[15:0]
//coe_r[15:0]
always@(posedge I_sclk)
    if(step[0])
    begin
        base_r <= I_gamma_ram_q[15:0 ];
        coe_r  <= I_gamma_ram_q[31:16];
    end

//base_g[15:0]
//coe_g[15:0]
always@(posedge I_sclk)
    if(step[1])
    begin
        base_g <= I_gamma_ram_q[15:0 ];
        coe_g  <= I_gamma_ram_q[31:16];
    end

//base_b[15:0]
//coe_b[15:0]
always@(posedge I_sclk)
    if(step[2])
    begin
        base_b <= I_gamma_ram_q[15:0 ];
        coe_b  <= I_gamma_ram_q[31:16];
    end

//comp0/1/2
always@(posedge I_sclk or negedge I_rst_n)
    if(!I_rst_n)
    begin
        comp0 <= 'b0;
        comp1 <= 'b0;
        comp2 <= 'b0;
    end
    else if(step[2])
    begin
        comp0 <= (grey_buf2 > grey_buf1);
        comp1 <= (grey_buf2 > grey_buf0);
        comp2 <= (grey_buf1 > grey_buf0);
    end

//base_buf[15:0]
always@(posedge I_sclk or negedge I_rst_n)
    if(!I_rst_n)
        base_buf <= 'b0;
    else if(step[3])
        base_buf <= base_r;
    else if(step[4])
        base_buf <= base_g;
    else if(step[5])
        base_buf <= base_b;

//coe_buf[15:0]
always@(posedge I_sclk or negedge I_rst_n)
    if(!I_rst_n)
        coe_buf <= 1'b0;
    else if(I_cfg_color_restore)
    begin
        if(step[3])
        begin
            if(comp0) // r > g
            begin
                if(comp1) // r > b
                    coe_buf <= coe_r;
                else
                    coe_buf <= coe_b;
            end
            else     // r < g
            begin
                if(comp2) // g > b
                    coe_buf <= coe_g;
                else
                    coe_buf <= coe_b;
            end
        end
    end
    else if(step[3])
        coe_buf <= coe_r;
    else if(step[4])
        coe_buf <= coe_g;
    else if(step[5])
        coe_buf <= coe_b;

//result_buf[31:0]
always@(posedge I_sclk or negedge I_rst_n)
    if(!I_rst_n)
        result_buf <= 'b0;
    else
        result_buf <= base_buf * coe_buf;

//result_val[15:0]
assign result_val = result_buf[COE_FRAC+15:COE_FRAC];

//incr_buf[15:0]
always@(posedge I_sclk or negedge I_rst_n)
    if(!I_rst_n)
        incr_buf <= 'b0;
    else if(!I_cfg_gamma_enable)
        incr_buf <= {grey_buf4, grey_buf4};
    else if(!I_cfg_gamma_incr || I_cfg_gamma_bit<=7) // 不补偿
        incr_buf <= 'b0;
    else
    case(I_cfg_gamma_bit)
        8:  incr_buf <= {grey_buf4, 7'b0};  // 9bit灰度
        9:  incr_buf <= {grey_buf4, 6'b0};  // 10bit灰度
        10: incr_buf <= {grey_buf4, 5'b0};  // 11bit灰度
        11: incr_buf <= {grey_buf4, 4'b0};  // 12bit灰度
        12: incr_buf <= {grey_buf4, 3'b0};  // 13bit灰度
        13: incr_buf <= {grey_buf4, 2'b0};  // 14bit灰度
        14: incr_buf <= {grey_buf4, 1'b0};  // 15bit灰度
        15: incr_buf <=  grey_buf4;         // 16bit灰度
    endcase

//gamma_buf[15:0]
always@(posedge I_sclk or negedge I_rst_n)
    if(!I_rst_n)
        gamma_buf <= 'b0;
    else if(I_cfg_gamma_enable && result_val>incr_buf)
        gamma_buf <= result_val;
    else
        gamma_buf <= incr_buf;

endmodule

`default_nettype wire

// vim:set ts=4 sw=4 et fenc=utf-8 fdm=marker:
